<?php

namespace app\controller;

use app\BaseController;
use think\facade\Filesystem;
use PhpOffice\PhpSpreadsheet\IOFactory;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Writer\Xlsx;
use think\Request;
use think\Response;
use app\common\Snowflake;
use think\facade\Db;
use League\Csv\Reader;
use think\exception\HttpResponseException;

class Activity extends BaseController
{
    // 新增活动
    public function create()
    {
        $data = $this->request->post();
        $snowflake = new Snowflake(1, 1);
        $data['activity_id'] = $snowflake->nextId();

        $validate = $this->validate($data, [
            'name' => 'require',
            'jump' => 'require|in:1,2,3',
            'jump_url' => 'require',
            'background_image' => 'require',
            'button_image' => 'require',
            'popup_time' => 'require|in:1,2,3',
            'target_audience' => 'require|in:1,2,3,4',
            'start_time' => 'require',
            'end_time' => 'require'
        ]);
        if ($validate !== true) {
            return json([
                'code' => 1,
                'msg' => $validate,
                'data' => []
            ]);
        }
        $result = Db::name('activity')->insert($data);
        if ($result) {
            return json([
                'code' => 0,
                'msg' => '活动新增成功',
                'data' => []
            ]);
        } else {
            return json([
                'code' => 1,
                'msg' => '活动新增失败',
                'data' => []
            ]);
        }
    }

    // 编辑活动
    public function update($id)
    {
        $data = $this->request->post();
        $validate = $this->validate($data, [
            'name' => 'require',
            'jump' => 'require|in:1,2,3',
            'jump_url' => 'require',
            'background_image' => 'require',
            'button_image' => 'require',
            'popup_time' => 'require|in:1,2,3',
            'target_audience' => 'require|in:1,2,3,4',
            'start_time' => 'require',
            'end_time' => 'require'
        ]);
        if ($validate !== true) {
            return json([
                'code' => 1,
                'msg' => $validate,
                'data' => []
            ]);
        }
        $result = Db::name('activity')->where('id', $id)->update($data);
        if ($result !== false) {
            return json([
                'code' => 0,
                'msg' => 'ok',
                'data' => []
            ]);
        } else {
            return json([
                'code' => 1,
                'msg' => '活动编辑失败',
                'data' => []
            ]);
        }
    }

    // 删除活动
    public function delete($id)
    {
        $result = Db::name('activity')->where('id', $id)->delete();
        if ($result) {
            return json([
                'code' => 0,
                'msg' => '活动删除成功',
                'data' => []
            ]);
        } else {
            return json([
                'code' => 1,
                'msg' => '活动删除失败',
                'data' => []
            ]);
        }
    }

    // 活动列表
    public function list()
    {
        $activityId = $this->request->get('activity_id');
        $name = $this->request->get('name');
        $status = $this->request->get('status');
        $page = max(1, (int)$this->request->get('page', 1)); // 最小页码1
        $limit = max(5, min(50, (int)$this->request->get('limit', 10))); // 限制5-50条/页

        $query = Db::name('activity');

        // 搜索条件
        if ($activityId) $query->where('activity_id', $activityId);
        if ($name) $query->where('name', 'like', "%$name%");

        // 状态过滤（保留原逻辑）
        if ($status) {
            switch ($status) {
                case 'not_started': $query->where('start_time', '>', date('Y-m-d')); break;
                case 'ongoing': $query->where('start_time', '<=', date('Y-m-d'))->where('end_time', '>=', date('Y-m-d')); break;
                case 'ended': $query->where('end_time', '<', date('Y-m-d')); break;
                case 'offline': $query->where('is_published', 0); break;
            }
        }

        // 分页查询（使用ThinkPHP内置分页）
        $result = $query->order('create_time DESC')
            ->paginate([
                'page'     => $page,
                'list_rows'=> $limit,
                'var_page' => 'page' // 自定义分页变量名
            ]);

        // 数据格式化（保留原状态计算逻辑）
        $list = $result->items();
        foreach ($list as &$item) {
            $item['status'] = $item['is_published'] ?
                ($item['start_time'] > date('Y-m-d') ? '未开始' :
                    ($item['end_time'] >= date('Y-m-d') ? '进行中' : '已结束'))
                : '下线';
        }

        // 构建分页响应
        return json([
            'code' => 0,
            'msg'  => 'ok',
            'data' => [
                'list'       => $list,
                'page_info'  => [
                    'current_page' => $result->currentPage(),
                    'last_page'    => $result->lastPage(),
                    'total'        => $result->total(),
                    'per_page'     => $result->listRows(),
                    'has_more'     => $result->currentPage() < $result->lastPage()
                ]
            ]
        ]);
    }

    // 导出活动数据
    public function export()
    {
        $activityId = $this->request->get('activity_id');
        $name = $this->request->get('name');
        $status = $this->request->get('status');

        $query = Db::name('activity');
        if ($activityId) {
            $query->where('activity_id', $activityId);
        }
        if ($name) {
            $query->where('name', 'like', "%$name%");
        }
        if ($status) {
            switch ($status) {
                case 'not_started':
                    $query->where('start_time', '>', date('Y-m-d'));
                    break;
                case 'ongoing':
                    $query->where('start_time', '<=', date('Y-m-d'))
                        ->where('end_time', '>=', date('Y-m-d'));
                    break;
                case 'ended':
                    $query->where('end_time', '<', date('Y-m-d'));
                    break;
                case 'offline':
                    $query->where('is_published', 0);
                    break;
            }
        }
        $list = $query->select();
        foreach ($list as &$item) {
            if ($item['start_time'] > date('Y-m-d')) {
                $item['status'] = '未开始';
            } elseif ($item['start_time'] <= date('Y-m-d') && $item['end_time'] >= date('Y-m-d')) {
                $item['status'] = '进行中';
            } elseif ($item['end_time'] < date('Y-m-d')) {
                $item['status'] = '已结束';
            }
            if ($item['is_published'] == 0) {
                $item['status'] = '下线';
            }
        }

        $spreadsheet = new Spreadsheet();
        $sheet = $spreadsheet->getActiveSheet();
        $sheet->setCellValue('A1', 'ID');
        $sheet->setCellValue('B1', '名称');
        $sheet->setCellValue('C1', '缩略图');
        $sheet->setCellValue('D1', '开始时间');
        $sheet->setCellValue('E1', '结束时间');
        $sheet->setCellValue('F1', '发布状态');
        $sheet->setCellValue('G1', '修改时间');

        $row = 2;
        foreach ($list as $item) {
            $sheet->setCellValue('A' . $row, $item['activity_id']);
            $sheet->setCellValue('B' . $row, $item['name']);
            $sheet->setCellValue('C' . $row, $item['background_image']);
            $sheet->setCellValue('D' . $row, $item['start_time']);
            $sheet->setCellValue('E' . $row, $item['end_time']);
            $sheet->setCellValue('F' . $row, $item['is_published']);
            $sheet->setCellValue('G' . $row, $item['update_time']);
            $row++;
        }

        $writer = new Xlsx($spreadsheet);
        header('Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
        header('Content-Disposition: attachment;filename="activity_export.xlsx"');
        header('Cache-Control: max-age=0');
        $writer->save('php://output');
    }

    // 表头别名映射（含大小写/全半角容错）
    private $headerMap = [
        'activity_id' => ['活动ID', '雪花ID', 'ID', '活动编号', '唯一ID', 'ACTIVITYID'],
        'name' => ['活动名称', '名称', '标题', 'TITLE', 'NAME'],
        'jump' => ['跳转类型', '跳转方式', '类型', 'JUMP'],
        'jump_url' => ['跳转链接', 'URL', '链接', 'LINK'],
        'background_image' => ['背景图', '背景图片', 'BACKGROUND'],
        'button_image' => ['按钮图', '按钮图片', 'BUTTON'],
        'popup_time' => ['弹出时机', '触发时机', '时机', 'TIME'],
        'target_audience' => ['目标人群', '受众', '人群', 'AUDIENCE'],
        'start_time' => ['开始时间', '生效时间', 'START'],
        'end_time' => ['结束时间', '截止时间', 'END'],
        'countdown' => ['倒计时', '剩余小时', 'HOURS'],
        'is_published' => ['发布状态', '是否发布', 'PUBLISHED']
    ];

    // 识别的空值（Excel/CSV/TXT）
    private $nullValues = ['#NULL!', 'null', 'nil', '', ' ', '　', 'NaN']; // 增加NaN处理

    // 枚举值映射（支持中文/数字/英文混合输入）
    private $enumMap = [
        'jump' => ['无'=>1, '端内'=>2, '端外'=>3, '1'=>1, '2'=>2, '3'=>3, 'none'=>1, 'in'=>2, 'out'=>3],
        'popup_time' => ['登录首页'=>1, '购买成功'=>2, '端内页面'=>3, '1'=>1, '2'=>2, '3'=>3, 'login'=>1, 'buy'=>2, 'page'=>3],
        'target_audience' => ['普通用户'=>1, '时效会员'=>2, 'VIP用户'=>3, '所有用户'=>4, '1'=>1, '2'=>2, '3'=>3, '4'=>4, 'all'=>4]
    ];

    // 文件解析器（依赖league/csv，或使用原生实现）
    private function parseFile(string $filePath, string $ext): array
    {
        $file = new SplFileObject($filePath);
        $file->setFlags(SplFileObject::SKIP_EMPTY | SplFileObject::READ_AHEAD);

        // 处理不同文件类型
        if (in_array($ext, ['xls', 'xlsx'])) {
            // 需安装phpoffice/phpspreadsheet
            $reader = \PhpOffice\PhpSpreadsheet\IOFactory::createReaderByExtension($ext);
            $spreadsheet = $reader->load($filePath);
            $sheet = $spreadsheet->getActiveSheet();
            $rows = $sheet->toArray(null, true, true, true);
            $headers = array_shift($rows);
        } else {
            // 处理CSV/TXT
            $file->setCsvControl($ext === 'csv' ? ',' : "\t");
            $headers = $file->fgetcsv();
            $rows = [];
            while (!$file->eof()) {
                $rows[] = $file->fgetcsv();
            }
        }

        // 过滤空行
        $rows = array_filter($rows, fn($row) => !empty(array_filter($row, fn($v) => !in_array($v, $this->nullValues))));

        return [$headers, $rows];
    }

    public function import(Request $request): Response
    {
        $file = $request->file('file');
        if (!$file) return $this->fail('请选择文件');

        $ext = strtolower($file->getOriginalExtension());
        if (!in_array($ext, ['txt', 'csv', 'xls', 'xlsx'])) {
            return $this->fail('仅支持txt/csv/xls/xlsx');
        }

        $savePath = runtime_path('import/activity/') . date('Ymd/');
        $this->createDir($savePath);
        $filename = uniqid() . '.' . $ext;
        $file->move($savePath, $filename);
        $filePath = $savePath . $filename;

        try {
            [$headers, $rows] = $this->parseFile($filePath, $ext);
            $this->validateHeaders($headers);
            $result = $this->processRows($rows, $headers, $filePath);
            return $this->success(
                "成功导入{$result['success']}条",
                $result['errors']
            );
        } catch (\Exception $e) {
            @unlink($filePath);
            return $this->fail("导入失败：{$e->getMessage()}");
        }
    }

    private function processRows(array $rows, array $headers, string $filePath): array
    {
        $success = 0;
        $errors = [];
        $snowflake = new Snowflake(1, 1);
        $batchData = [];
        $currentId = null;

        Db::startTrans();
        try {
            foreach ($rows as $rowNum => $row) {
                $rowIndex = $rowNum + 2; // 标题行是第1行

                // 生成ID（带缓存）
                if (is_null($currentId)) {
                    $currentId = $this->generateActivityId($row, $headers, $snowflake, $rowIndex);
                }

                $data = [
                    'activity_id' => $currentId,
                    'name' => $this->getString($row, $headers, 'name', $rowIndex, false),
                    'jump' => $this->getEnum($row, $headers, 'jump', $rowIndex),
                    'jump_url' => $this->getUrl($row, $headers, 'jump_url', $rowIndex, true),
                    'background_image' => $this->getImage($row, $headers, 'background_image', $rowIndex, true),
                    'button_image' => $this->getImage($row, $headers, 'button_image', $rowIndex, true),
                    'popup_time' => $this->getEnum($row, $headers, 'popup_time', $rowIndex),
                    'target_audience' => $this->getEnum($row, $headers, 'target_audience', $rowIndex),
                    'start_time' => $this->getDateTime($row, $headers, 'start_time', $rowIndex),
                    'end_time' => $this->getDateTime($row, $headers, 'end_time', $rowIndex),
                    'countdown' => $this->getInt($row, $headers, 'countdown', $rowIndex, true),
                    'is_published' => $this->getBool($row, $headers, 'is_published', $rowIndex, true),
                    'create_time' => time(),
                    'update_time' => time()
                ];

                // 唯一性校验
                if ($this->checkDuplicate($currentId, $rowIndex)) {
                    throw new \Exception("活动ID重复：{$currentId}", $rowIndex);
                }

                $batchData[] = $data;
                if (count($batchData) >= 200) { // 优化批次大小
                    $this->batchInsert($batchData);
                    $batchData = [];
                    $success += 200;
                    $currentId = null; // 重置ID缓存
                }
            }

            // 处理剩余数据
            if (!empty($batchData)) {
                $this->batchInsert($batchData);
                $success += count($batchData);
            }

            Db::commit();
            @unlink($filePath);
            return compact('success', 'errors');

        } catch (\Exception $e) {
            Db::rollback();
            $errors[] = [
                'row' => $e->getCode(),
                'msg' => $e->getMessage(),
                'field' => $this->getHumanField($e->getMessage())
            ];
            return compact('success', 'errors');
        }
    }

    // === 核心业务方法 ===
    private function generateActivityId(array $row, array $headers, Snowflake $snowflake, int $rowIndex): string
    {
        $header = $this->matchHeader('activity_id', $headers);
        $value = $this->cleanValue($row[$header] ?? '');

        if ($this->isNullOrEmpty($value)) {
            // 数据库分布式锁（5秒超时）
            if (!Db::query('SELECT GET_LOCK("activity_import", 5)')[0]['GET_LOCK']) {
                throw new \Exception('导入锁获取失败', $rowIndex);
            }

            try {
                do {
                    $id = $snowflake->nextId();
                } while (Db::name('activity')->where('activity_id', $id)->find());
                return (string)$id;
            } finally {
                Db::query('SELECT RELEASE_LOCK("activity_import")');
            }
        }

        if (!$this->validateSnowflakeId($value)) {
            throw new \Exception('活动ID需为17-20位数字', $rowIndex);
        }
        return $value;
    }

    // === 数据转换方法 ===
    private function getEnum(array $row, array $headers, string $field, int $rowIndex): int
    {
        $header = $this->matchHeader($field, $headers);
        $value = $this->cleanValue($row[$header] ?? '');
        $map = $this->enumMap[$field] ?? [];

        if ($this->isNullOrEmpty($value)) {
            throw new \Exception("缺少{$this->getHumanField($field)}", $rowIndex);
        }

        $key = mb_strtolower($value);
        if (isset($map[$value])) return $map[$value];
        if (isset($map[$key])) return $map[$key];

        throw new \Exception("{$this->getHumanField($field)}值无效（允许：" . implode('/', array_keys($map)) . "）", $rowIndex);
    }

    private function getUrl(array $row, array $headers, string $field, int $rowIndex, bool $allowEmpty): ?string
    {
        $value = $this->getString($row, $headers, $field, $rowIndex, $allowEmpty);
        if ($allowEmpty && $this->isNullOrEmpty($value)) return null;
        if (!filter_var($value, FILTER_VALIDATE_URL)) {
            throw new \Exception("{$this->getHumanField($field)}需为有效URL", $rowIndex);
        }
        return $value;
    }

    private function getImage(array $row, array $headers, string $field, int $rowIndex, bool $allowEmpty): ?string
    {
        $value = $this->getString($row, $headers, $field, $rowIndex, $allowEmpty);
        if ($allowEmpty && $this->isNullOrEmpty($value)) return null;
        if (!preg_match('/\.(jpg|jpeg|png|gif|webp)$/i', $value)) {
            throw new \Exception("{$this->getHumanField($field)}需为图片链接", $rowIndex);
        }
        return $value;
    }

    private function getDateTime(array $row, array $headers, string $field, int $rowIndex): string
    {
        $value = $this->getString($row, $headers, $field, $rowIndex);
        $timestamp = strtotime($value);
        if (!$timestamp) {
            throw new \Exception("{$this->getHumanField($field)}格式错误（示例：2024-07-01 09:00）", $rowIndex);
        }
        return date('Y-m-d H:i:s', $timestamp);
    }

    // === 基础校验方法 ===
    private function validateHeaders(array $headers): void
    {
        $lowerHeaders = array_map('mb_strtolower', $headers);
        foreach ($this->headerMap as $field => $aliases) {
            if ($field === 'activity_id') continue; // 允许自动生成
            $found = false;
            foreach ($aliases as $alias) {
                if (in_array(mb_strtolower($alias), $lowerHeaders)) {
                    $found = true;
                    break;
                }
            }
            if (!$found) {
                throw new \Exception("缺少必要表头：{$this->getHumanField($field)}");
            }
        }
    }

    // === 工具方法 ===
    private function getString(array $row, array $headers, string $field, int $rowIndex, bool $allowEmpty): string
    {
        $header = $this->matchHeader($field, $headers);
        $value = $row[$header] ?? '';
        $cleanValue = $this->cleanValue($value);

        if (!$allowEmpty && $this->isNullOrEmpty($cleanValue)) {
            throw new \Exception("缺少{$this->getHumanField($field)}", $rowIndex);
        }
        return $cleanValue;
    }

    private function cleanValue($value): string
    {
        if (!is_string($value)) return '';
        return mb_convert_kana(trim($value), 'aKV'); // 全半角转换+去空格
    }

    private function isNullOrEmpty($value): bool
    {
        return in_array($this->cleanValue($value), $this->nullValues);
    }

    // === 数据库操作 ===
    private function batchInsert(array $data): void
    {
        if (empty($data)) return;
        Db::name('activity')->insertAll($data);
    }

    private function checkDuplicate(string $activityId, int $rowIndex): bool
    {
        return Db::name('activity')->where('activity_id', $activityId)->find() ? true : false;
    }

    // === 辅助方法 ===
    private function matchHeader(string $field, array $headers): string
    {
        foreach ($this->headerMap[$field] as $alias) {
            foreach ($headers as $h) {
                if (mb_strtolower($h) === mb_strtolower($alias)) {
                    return $h;
                }
            }
        }
        throw new \Exception("未找到匹配的表头：{$this->getHumanField($field)}");
    }

    private function getHumanField(string $msg): string
    {
        $map = [
            'activity_id' => '活动ID',
            'name' => '活动名称',
            'jump' => '跳转类型',
            'jump_url' => '跳转链接',
            'background_image' => '背景图',
            'button_image' => '按钮图',
            'popup_time' => '弹出时机',
            'target_audience' => '目标人群',
            'start_time' => '开始时间',
            'end_time' => '结束时间',
            'countdown' => '倒计时',
            'is_published' => '发布状态'
        ];
        foreach ($map as $field => $human) {
            if (strpos($msg, $field) !== false) {
                return $human;
            }
        }
        return '字段';
    }

    private function createDir(string $path): void
    {
        if (!is_dir($path)) {
            mkdir($path, 0755, true);
        }
    }

    private function fail(string $msg): Response
    {
        return json([
            'code' => 400,
            'msg' => $msg,
            'time' => time()
        ]);
    }

    private function success(string $msg, array $errors): Response
    {
        return json([
            'code' => 200,
            'msg' => $msg,
            'data' => [
                'total' => $errors ? count($errors) : 0,
                'errors' => $errors
            ],
            'time' => time()
        ]);
    }



}